//
//  blePeripheral.m
//  MonitoringCenter
//
//  Created by David ding on 13-1-10.
//
//

#import "BLEPeripheral.h"
#import "CBUUID+Makeblock.h"


// 一次性最大可以发送的数据包长度
#define NOTIFY_MTU                      20

@interface BLEPeripheral()<CBPeripheralDelegate>

//readonly
@property(nonatomic,strong) CBService* transDataService;
@property(nonatomic,strong) CBService* resetService;
@property(nonatomic,strong) CBService* resetService2;
@property(nonatomic,strong) CBService* resetService3;
@property(nonatomic,strong) CBCharacteristic* transDataCharateristic;
@property(nonatomic,strong) CBCharacteristic* resetCharateristic;
@property(nonatomic,strong) CBCharacteristic* resetCharateristic2;
@property(nonatomic,strong) CBCharacteristic* resetCharateristic3;
@property(nonatomic,strong) CBCharacteristic* resetCharateristic4;
@property(nonatomic,strong) CBCharacteristic* escapeCheckCharateristic;
@property(nonatomic,assign) BLEPeripheralDelegateState currentPeripheralState;
@property(nonatomic,strong) NSString* nameString;
@property(nonatomic,strong) NSString* displayNameString;
@property(nonatomic,strong) NSString* uuidString;
@property(nonatomic,assign) BOOL connectedFinish;
@property(nonatomic,strong) NSData* receiveData;

//others
@property (nonatomic, assign) BOOL isDual;

@end

@implementation BLEPeripheral

#pragma mark -- init

-(instancetype)initWithPeripheral:(CBPeripheral *)peripheral{
    self = [super init];
    if (self) {
        [self _commonInit];
        _activePeripheral = peripheral;
    }
    return self;
}

-(id)init{
    self = [super init];
    if (self) {
        [self _commonInit];
    }
    return self;
}

-(void)_commonInit{
    [self _initPeripheralWithSeviceAndCharacteristic];
    [self _initProperty];
    self.buffers = [NSMutableArray array];
    self.isDual = NO;
    enWrite = NO;
    enNotify = NO;
}

-(void)_initPeripheralWithSeviceAndCharacteristic{
    _activePeripheral.delegate = nil;
    self.activePeripheral = nil;
    self.transDataService = nil;
    self.resetService = nil;
    self.resetCharateristic = nil;
    self.transDataCharateristic = nil;
}

-(void)_initProperty{
    self.currentPeripheralState = BLEPeripheralDelegateStateInit;
    [[NSNotificationCenter defaultCenter]postNotificationName:@"CBPeripheralStateChange" object:nil];
    self.connectedFinish = NO;
    self.receiveData = 0;
    self.sendData = 0;
    self.txCounter = 0;
    self.rxCounter = 0;
    self.showStringBuffer = [NSString string];
}

#pragma mark -- setters && getters

-(void)setActivePeripheral:(CBPeripheral *)activePeripheral{
    _activePeripheral = activePeripheral;
    
    NSString *aname = [[NSString alloc]initWithFormat:@"%@",_activePeripheral.name];
    if (![aname isEqualToString:@"(null)"]) {
        self.nameString = aname;
        
    }else{
        self.nameString = @"Error Name";
    }
    
    if (_activePeripheral.identifier.UUIDString.length >= 36) {
        self.uuidString = [_activePeripheral.identifier.UUIDString substringWithRange:NSMakeRange(_activePeripheral.identifier.UUIDString.length-36, 36)];
    }
}

#pragma mark -- public methods

-(CGFloat)distanceByRSSI {
    if (self.RSSI) {
        return powf(10.0,((abs(self.RSSI.intValue)-50.0)/50.0))*0.7;
    }
    return -1.0f;
}

-(NSString *)displayName{
    if (_displayNameString) {
        return _displayNameString;
    }
    
    NSString* rectStrig = nil;
    NSString* nameString = self.activePeripheral.name;
    if ([nameString.lowercaseString hasPrefix:@"makeblock"]) {
        if ([nameString.lowercaseString isEqualToString:@"makeblock"] || [nameString.lowercaseString isEqualToString:@"makeblock_le"]) {
            rectStrig = @"Makeblock";
        }else{
            rectStrig = [[nameString stringByReplacingOccurrencesOfString:@"Makeblock_LE" withString:@""] stringByReplacingOccurrencesOfString:@"Makeblock_" withString:@""];
        }
    }
    
    self.displayNameString = rectStrig;
    
    return _displayNameString;
}

-(void)startPeripheral:(CBPeripheral *)peripheral{
    if(peripheral != _activePeripheral || peripheral.state != CBPeripheralStateConnected) return;
    
    _activePeripheral = peripheral;
    [_activePeripheral setDelegate:self];
    [_activePeripheral discoverServices:nil];
}

-(void)disconnectActivePeripheral{
    // 内存释放
    [self _initPeripheralWithSeviceAndCharacteristic];
    [self _initProperty];
}

-(void)sendData:(NSData *)data{
    [_buffers addObject:data];
    [self _execute];
}

-(void)sendDataMandatory:(NSData *)data{
    [self _writeValue:_activePeripheral characteristic:_transDataCharateristic data:data];
}

-(void)setAirblockGPIO:(NSInteger)gpio isHighLevel:(BOOL)isHighLevel{
    NSData* data = nil;
    if (isHighLevel) {
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x01} length:1];
    }else{
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x00} length:1];
    }
    
    CBCharacteristic* charateristic = nil;
    if (gpio == 1) {
        charateristic = _resetCharateristic;
    }
    else if (gpio == 2){
        charateristic = _resetCharateristic2;
    }
    
    if (charateristic) {
        [self _writeValue:_activePeripheral characteristic:charateristic data:data];
    }
}

-(void)setAirblockEscapeCheck:(BOOL)escape{
    NSData* data = nil;
    if (escape) {
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x01} length:1];
    }else{
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x00} length:1];
    }
    
    if (_escapeCheckCharateristic) {
        [self _writeValue:_activePeripheral characteristic:_escapeCheckCharateristic data:data];
    }
}

- (void)setCommonGPIO:(NSInteger)gpio isHigh:(BOOL)isHigh{
    NSData* data = nil;
    if (isHigh) {
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x01} length:1];
    }else{
        data = [NSMutableData dataWithBytes:(u_int8_t[]){0x00} length:1];
    }
    
    CBCharacteristic* charateristic = nil;
    if (gpio == 1) {
        charateristic = _resetCharateristic;
    }
    else if (gpio == 2){
        charateristic = _resetCharateristic2;
    }
    else if (gpio == 3){
        charateristic = _resetCharateristic3;
    }
    else if (gpio == 4){
        charateristic = _resetCharateristic4;
    }
    
    if (charateristic) {
        [self _writeValue:_activePeripheral characteristic:charateristic data:data];
    }
}

-(void)readData{
    [self _readValue:_activePeripheral characteristicUUID:_transDataCharateristic];
}

-(void)resetIO{
    if(!_resetCharateristic) return;
    
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        //code to be executed on the main queue after
        char i = 1;
        NSData *data = [NSData dataWithBytes: &i length: sizeof(i)];
        NSLog(@"reseting...");
        [_activePeripheral writeValue:data forCharacteristic:_resetCharateristic type:CBCharacteristicWriteWithoutResponse];
    });
    dispatch_time_t popTime2 = dispatch_time(DISPATCH_TIME_NOW, 0.10 * NSEC_PER_SEC);
    dispatch_after(popTime2, dispatch_get_main_queue(), ^(void){
        //code to be executed on the main queue after
        
        char i=0;
        NSData *data = [NSData dataWithBytes: &i length: sizeof(i)];
        NSLog(@"reseting 2...");
        [_activePeripheral writeValue:data forCharacteristic:_resetCharateristic type:CBCharacteristicWriteWithoutResponse];
    });
    dispatch_time_t popTime3 = dispatch_time(DISPATCH_TIME_NOW, 0.20 * NSEC_PER_SEC);
    dispatch_after(popTime3, dispatch_get_main_queue(), ^(void){
        //code to be executed on the main queue after
        
        char i=1;
        NSData *data = [NSData dataWithBytes: &i length: sizeof(i)];
        NSLog(@"reseting 3...");
        [_activePeripheral writeValue:data forCharacteristic:_resetCharateristic type:CBCharacteristicWriteWithoutResponse];
    });
}

#pragma mark -- CBPeripheralDelegate

/**
 *  扫描服务
 *
 *  @param peripheral 外围设备
 *  @param error      错误信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    if (error || peripheral != _activePeripheral) return;
    
    NSArray *services = [peripheral services];
    if (!services || ![services count]){
        NSLog(@"发现错误的服务 %@\r\n", peripheral.services);
        
    }else{
        self.currentPeripheralState = BLEPeripheralDelegateStateDiscoverServices;
        [[NSNotificationCenter defaultCenter]postNotificationName:@"CBPeripheralStateChange" object:nil];
        
        for (CBService *service in peripheral.services){
            // 扫描接收数据服务特征值
            if ([[service UUID] isEqual:[CBUUID transDataService]]){
                self.transDataService = service;
                [peripheral discoverCharacteristics:nil forService:_transDataService];
            }
            else if([[service UUID] isEqual:[CBUUID transDataDualService]]){
                self.transDataService = service;
                [peripheral discoverCharacteristics:nil forService:_transDataService];
                self.isDual = YES;
            }
            else if([[service UUID] isEqual:[CBUUID resetDualService]]){
                self.resetService = service;
                [peripheral discoverCharacteristics:nil forService:_resetService];
            }
        }
    }
}

/**
 *  从服务中扫描特征值
 *
 *  @param peripheral 外部设备
 *  @param service    服务
 *  @param error      错误信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    if (error || peripheral != _activePeripheral) return;
    
    //开始扫描特征值
    self.currentPeripheralState = BLEPeripheralDelegateStateDiscoverCharacteristics;
    [[NSNotificationCenter defaultCenter]postNotificationName:@"CBPeripheralStateChange" object:nil];
    
    NSArray *characteristics = [service characteristics];
    CBCharacteristic *characteristic;
    
    if ([[service UUID] isEqual:(_isDual ? [CBUUID transDataDualService] :[CBUUID transDataService])]){
        for (characteristic in characteristics){
            NSLog(@"发现特值UUID: %@ - %@\n", [characteristic UUID],service);
            
            if ([[characteristic UUID] isEqual:(_isDual ? [CBUUID transDataDualCharateristic] : [CBUUID transDataCharateristic])]){
                self.transDataCharateristic = characteristic;
                enWrite = YES;
                if(enNotify){
                    [self _finishConnected];
                }
                
            }else if ([[characteristic UUID] isEqual:(_isDual? [CBUUID nofityDataDualCharateristic]:[CBUUID nofityDataCharateristic])]){
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
                enNotify = YES;
                if(enWrite){
                    [self _finishConnected];
                }
                
            }
        }
    
    }else if([[service UUID] isEqual:[CBUUID resetDualService]]){
        for (characteristic in characteristics){
            NSLog(@"发现特值UUID: %@ - %@\n", [characteristic UUID],service);
            if([[characteristic UUID] isEqual:[CBUUID resetDualCharateristic]]){
                self.resetCharateristic = characteristic;
            }
            else if([[characteristic UUID] isEqual:[CBUUID resetDualCharateristic2]]){
                self.resetCharateristic2 = characteristic;
            }
            if([[characteristic UUID] isEqual:[CBUUID resetDualCharateristic3]]){
                self.resetCharateristic3 = characteristic;
            }
            else if([[characteristic UUID] isEqual:[CBUUID resetDualCharateristic4]]){
                self.resetCharateristic4 = characteristic;
            }
            else if([[characteristic UUID] isEqual:[CBUUID escapeCheckCharateristic]]){
                self.escapeCheckCharateristic = characteristic;
            }
        }
    }
}

/**
 *  更新特征值
 *
 *  @param peripheral     外围设备
 *  @param characteristic 特征值
 *  @param error          错误信息
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if(error.code != 0 || peripheral != _activePeripheral) return;
    
    if ([characteristic.UUID isEqual:(_isDual ? [CBUUID nofityDataDualCharateristic] : [CBUUID nofityDataCharateristic])]){
        NSData * rx =characteristic.value;
        [self _receiveData:rx];
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error{
    if (_delegate && [_delegate respondsToSelector:@selector(didPeripheral:updateRSSI:)]) {
        [_delegate didPeripheral:peripheral updateRSSI:RSSI];
    }
}

#pragma mark -- private methods

/**
 *  收到数据
 *
 *  @param data 数据
 */
-(void)_receiveData:(NSData *)data{
    if(data.length==0){
        return;
    }
    
    [self _execute];
    
    [[NSNotificationCenter defaultCenter]postNotificationName:@"CBUpdataShowStringBuffer" object:nil userInfo:@{@"data":data}];
}

/**
 *  执行发送方法
 */
-(void)_execute{
    if(_buffers.count>0){
        NSData *data = [_buffers firstObject];
        NSLog(@"BLE->%@",[NSString stringWithCString:data.bytes encoding:NSUTF8StringEncoding]);
        
        [self _writeValue:_activePeripheral characteristic:_transDataCharateristic data:data];
        [_buffers removeObjectAtIndex:0];
    }
}

/**
 *  写数据到特征值
 *
 *  @param peripheral     外围设备
 *  @param characteristic 特征值
 *  @param data           数据
 */
-(void)_writeValue:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic data:(NSData *)data{
    // MODIFIED BY WANGYU 允许发送多于20字节的内容
    if ([peripheral isEqual:_activePeripheral] && peripheral.state==CBPeripheralStateConnected){
        if (characteristic != nil) {
            BOOL didSend = YES;//是否成功发送(或者说是否还有东西等待发送)
            NSInteger sendDataIndex = 0;
            while(didSend){//如果没有东西可发送，则上一次会发送失败
                NSInteger amountToSend = data.length - sendDataIndex;
                if(amountToSend > NOTIFY_MTU){
                    amountToSend = NOTIFY_MTU;
                }
                if(amountToSend <= 0){
                    return;
                }
                
                NSData *chunk = [NSData dataWithBytes:data.bytes+sendDataIndex length:amountToSend];
                [peripheral writeValue:chunk forCharacteristic:characteristic type:_isDual?CBCharacteristicWriteWithoutResponse:CBCharacteristicWriteWithResponse];
                
                sendDataIndex += amountToSend;
            }
        }
    }
}

/**
 *  连接完成
 */
-(void)_finishConnected{
    // 更新标志
    self.connectedFinish = YES;
    self.currentPeripheralState = BLEPeripheralDelegateStateKeepActive;
    
    NSLog(@"连接完成\n");
    [[NSNotificationCenter defaultCenter]postNotificationName:@"CBPeripheralStateChange" object:nil];
}

/**
 *  从特征值读取数据
 *
 *  @param peripheral     外围设备
 *  @param characteristic 特征值
 */
-(void)_readValue:(CBPeripheral *)peripheral characteristicUUID:(CBCharacteristic *)characteristic{
    if(peripheral != _activePeripheral || peripheral.state != CBPeripheralStateConnected || !characteristic) return;
    
    NSLog(@"成功从特征值:%@ 读数据\n", characteristic);
    [peripheral readValueForCharacteristic:characteristic];
}

/**
 *  发通知到特征值
 *
 *  @param peripheral     外围设备
 *  @param characteristic 特征值
 *  @param state
 */
-(void)_notification:(CBPeripheral *)peripheral characteristicUUID:(CBCharacteristic *)characteristic state:(BOOL)state{
    if(peripheral != _activePeripheral || peripheral.state != CBPeripheralStateConnected || !characteristic) return;
    
    NSLog(@"成功发通知到特征值: %@\n", characteristic);
    [peripheral setNotifyValue:state forCharacteristic:characteristic];
}

@end
